<?php

namespace app\common\helper;

use app\common\Constants;
use app\common\ErrorCode;
use app\common\exception\BusinessException;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;

class HttpHelper
{


    /**
     * 使用 Guzzle 发起一个 HTTP 请求
     *
     * @param string $url 请求的 URL
     * @param string $method HTTP 请求方法 (GET, POST, PUT, DELETE, etc.)
     * @param mixed $data 发送的数据。对于 GET 请求，会作为查询参数；对于 POST 等请求，会作为请求体。
     * @param array|null &$transferStats 可选，用于获取 Guzzle 传输统计信息的引用。
     * @param int $timeout 超时时间（秒）。
     * @param array $certInfo 证书信息数组，包含 keys: cert_file, cert_pass, cert_type, ca_file。
     * @return string|false 成功时返回响应体内容（字符串），失败时返回 false。
     */
    function httpRequest(
        string $url,
        string $method = 'GET',
               $data = null,
        ?array &$transferStats = null,
        int $timeout = 20,
        array $certInfo = []
    ): string|false {
        // 1. 初始化 Guzzle 客户端
        $client = new Client();
        $method = strtoupper($method);
        $options = [
            'timeout' => $timeout,
            'http_errors' => false, // 禁用 Guzzle 默认的 HTTP 状态码异常 ，以便手动处理 4xx/5xx 响应
            'on_stats' => function (\GuzzleHttp\TransferStats $stats) use (&$transferStats) {
                // 4. 通过 on_stats 回调获取详细的传输信息，替代 curl_getinfo
                $transferStats = [
                    'url' => $stats->getEffectiveUri()->__toString(),
                    'total_time' => $stats->getTransferTime(),
                    'http_code' => $stats->hasResponse( ) ? $stats->getResponse()->getStatusCode() : 0,
                    'connect_time' => $stats->getHandlerStat('connect_time'),
                    'primary_ip' => $stats->getHandlerStat('primary_ip'),
                ];
            }
        ];

        // 2. 根据请求方法处理数据
        if (!empty($data)) {
            if ($method === 'GET') {
                // 对于 GET 请求，数据作为查询字符串
                $options['query'] = is_array($data) ? $data : (string)$data;
            } else {
                // 对于 POST, PUT 等请求，Guzzle 会根据数据类型自动设置 Content-Type
                // 如果是数组，默认为 application/x-www-form-urlencoded
                // 如果想发送 JSON，应使用 'json' => $data
                $options['form_params'] = is_array($data) ? $data : [];
                if (!is_array($data)) {
                    $options['body'] = $data; // 发送原始请求体 (如 JSON 字符串, XML)
                }
            }
        }

        // 3. 处理证书信息
        if (!empty($certInfo)) {
            if (!empty($certInfo['cert_file'])) {
                // Guzzle 的 'cert' 选项可以是一个数组 [文件路径, 密码]
                $options['cert'] = [$certInfo['cert_file']];
                if (!empty($certInfo['cert_pass'])) {
                    $options['cert'][] = $certInfo['cert_pass'];
                }
            }
            if (!empty($certInfo['ca_file'])) {
                // 'verify' 选项用于 SSL 验证，可以直接指定 CA 包路径
                $options['verify'] = $certInfo['ca_file'];
            }
        }

        // Guzzle 默认会使用系统推荐的 CA 包进行验证 (verify => true)，行为与原 cURL 函数逻辑一致

        try {
            // 5. 发起请求
            $response = $client->request($method, $url, $options);

            // 检查 HTTP 状态码是否表示成功 (2xx)
            if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) {
                return (string) $response->getBody();
            } else {
                // 如果需要，可以记录非 2xx 的响应
                error_log(sprintf(
                    'Guzzle HTTP Error: Status %d for request to %s',
                    $response->getStatusCode(),
                    $url
                ));
                return false;
            }

        } catch (RequestException $e) {
            // 6. 捕获连接错误等请求异常 (如 DNS 解析失败、超时)
            error_log(sprintf(
                'Guzzle Request Exception: %s for request to %s',
                $e->getMessage(),
                $url
            ));
            // 即使请求失败，on_stats 回调也可能已经填充了 $transferStats
            if ($e->hasResponse()) {
                // 如果有响应，即使是错误响应，也可以获取其内容
                return (string) $e->getResponse()->getBody();
            }
            return false;
        }
    }


    /**
     * 获取请求的 Bearer Token
     *
     * @return string
     * @throws BusinessException
     */
    public static function getBearerToken(): string
    {
        $token = request()->header('Authorization');
        if (!$token || !preg_match('/^Bearer\s+(.*)$/i', $token, $matches)) {
            throw new BusinessException(Constants::E_COMMON_ACCESS_TOKEN_INVALID,ErrorCode::E_COMMON_ACCESS_TOKEN_INVALID);
        } else {
            return $matches[1];
        }
    }


    /**
     * 获取浏览器名称和版本
     *
     * @return string
     */
    public static function getBrowser(): string
    {
        $userAgent = request()->server('HTTP_USER_AGENT');

        $browser = 'Unknown';
        $version = '';

        // 浏览器匹配规则
        $browserPatterns = [
            'MSIE' => ['name' => 'Internet Explorer', 'key' => 'MSIE'],
            'Trident' => ['name' => 'Internet Explorer', 'key' => 'rv'], // IE11
            'Edge' => ['name' => 'Edge', 'key' => 'Edge'],
            'Edg' => ['name' => 'Edge', 'key' => 'Edg'], // 新版Edge
            'Firefox' => ['name' => 'Firefox', 'key' => 'Firefox'],
            'Chrome' => ['name' => 'Chrome', 'key' => 'Chrome'],
            'Safari' => ['name' => 'Safari', 'key' => 'Version'],
            'Opera' => ['name' => 'Opera', 'key' => 'Opera'],
            'OPR' => ['name' => 'Opera', 'key' => 'OPR'], // Opera 15+
            'Vivaldi' => ['name' => 'Vivaldi', 'key' => 'Vivaldi'],
            'UCBrowser' => ['name' => 'UCBrowser', 'key' => 'UCBrowser'],
            'QQBrowser' => ['name' => 'QQBrowser', 'key' => 'QQBrowser'],
            'WeChat' => ['name' => 'WeChat', 'key' => 'MicroMessenger'], // 微信内置浏览器
            'QQ' => ['name' => 'QQ', 'key' => 'QQ/'], // QQ内置浏览器
        ];

        foreach ($browserPatterns as $pattern => $info) {
            if (stripos($userAgent, $pattern) !== false) {
                $browser = $info['name'];

                // 提取版本号
                $versionKey = $info['key'];
                $pattern = '/' . $versionKey . '\/?([0-9.]+)/i';
                if (preg_match($pattern, $userAgent, $matches)) {
                    $version = $matches[1];
                }

                break;
            }
        }

        return $browser . '(' . $version . ')';
    }

    /**
     * 获取操作系统名称
     *
     * @return string
     */
    public static function getOs(): string
    {
        $userAgent = request()->server('HTTP_USER_AGENT');

        $osPlatform = "Unknown OS";

        // 常见操作系统匹配规则
        $osArray = [
            '/windows nt 10/i' => 'Windows 10',
            '/windows nt 6.3/i' => 'Windows 8.1',
            '/windows nt 6.2/i' => 'Windows 8',
            '/windows nt 6.1/i' => 'Windows 7',
            '/windows nt 6.0/i' => 'Windows Vista',
            '/windows nt 5.2/i' => 'Windows Server 2003/XP x64',
            '/windows nt 5.1/i' => 'Windows XP',
            '/windows xp/i' => 'Windows XP',
            '/windows nt 5.0/i' => 'Windows 2000',
            '/win98/i' => 'Windows 98',
            '/android/i' => 'Android',
            '/ubuntu/i' => 'Ubuntu',
            '/linux/i' => 'Linux',
            '/iphone|ipod|ipad/i' => 'iOS',
            '/macintosh|mac os x/i' => 'Mac OS X',
            '/mac_powerpc/i' => 'Mac OS 9',
            '/blackberry/i' => 'BlackBerry',
            '/webos/i' => 'Mobile'
        ];

        foreach ($osArray as $regex => $value) {
            if (preg_match($regex, $userAgent)) {
                $osPlatform = $value;
                break;
            }
        }

        return $osPlatform;
    }
}